<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>可点击选中单词的富文本</title>
    <style>
      /* 基础样式：保持原有富文本布局 */
      .rich-text-container {
        max-width: 800px;
        margin: 20px auto;
        padding: 20px;
        border: 1px solid #eee;
        border-radius: 8px;
      }
      .rich-text-container p {
        margin: 16px 0;
        line-height: 1.8;
        font-size: 16px;
      }
      .rich-text-container img {
        max-width: 100%;
        height: auto;
        margin: 10px 0;
        border-radius: 4px;
        border: 1px solid #f5f5f5;
      }

      /* 单词span样式：默认状态 + 点击选中状态 */
      .word-span {
        display: inline-block;
        padding: 2px 4px;
        margin: 0 2px;
        border-radius: 4px;
        cursor: pointer;
        transition: all 0.2s ease;
      }
      /* 鼠标悬浮提示 */
      .word-span:hover {
        background-color: #f0f7ff;
        color: #165dff;
      }
      /* 点击选中状态 */
      .word-span.active {
        background-color: #165dff;
        color: #fff;
      }
      /* 选中时的文本选中效果（避免浏览器默认选中样式冲突） */
      .word-span::selection {
        background-color: transparent;
      }
    </style>
  </head>
  <body>
    <!-- 富文本容器：初始加载原始富文本 -->
    <div class="rich-text-container" id="richTextContainer">
      <p style="background-color: #ccc;">
        'felis' <br> ___1___ tellus. mollis orci, sed rhoncus pronin sapien nunc accuan eget.
				<span>aaaa</span> 
      </p>
			<button>11111</button>
			<button>___2___</button>
      <p>felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
      <img src="../背景/000000.png" alt="000" />
      <p>felis tellus mollis orci, sed rhoncus pronin sapien nunc accuan eget.</p>
    </div>

    <script>
      document.addEventListener('DOMContentLoaded', () => {
        // 1. 获取富文本容器
        const richTextContainer = document.getElementById('richTextContainer')

        // 2. 核心函数：处理富文本，将单词包裹为可点击的span
        function wrapWordsWithClickableSpan() {
          // 递归处理所有节点：只操作文本节点，跳过图片等非文本标签
          function processNode(node) {
            // 情况1：当前是文本节点（且内容非空）
            if (
              node.nodeType === Node.TEXT_NODE &&
              node.textContent.trim() !== ''
            ) {
              const text = node.textContent
              // 按「空格」拆分单词（保留标点与单词的关联，如 orci,、eget.）
              // 正则说明：匹配「单词+可选标点」的组合，避免拆分后语义断裂
              const wordRegex = /\b[\w']+[.,!?;:"']?\b/g
              const parts = text.split(wordRegex) // 拆分出「非单词部分（空格/换行）」
              const words = text.match(wordRegex) || [] // 提取所有「单词+标点」
              console.log( words,  );
              console.log( '--------------------' );

              // 创建文档片段，避免频繁DOM操作
              const fragment = document.createDocumentFragment()
              let partIndex = 0

              // 交替插入「非单词部分」和「单词span」
              words.forEach((word) => {
                console.log( word );
                // 先插入前面的非单词部分（如空格）
                if (parts[partIndex] && parts[partIndex].trim() === '') {
                  fragment.appendChild(
                    document.createTextNode(parts[partIndex])
                  )
                  partIndex++
                }

                // 创建单词span，添加点击选中逻辑
                const wordSpan = document.createElement('span')
                wordSpan.className = 'word-span'
                wordSpan.textContent = word

								
                // 点击事件：选中当前单词（移除其他单词的选中状态）
                // wordSpan.addEventListener('click', (e) => {
                //   // 阻止事件冒泡，避免影响图片等其他元素
                //   e.stopPropagation()
                //   // 移除所有单词的选中状态
                //   document
                //     .querySelectorAll('.word-span.active')
                //     .forEach((span) => {
                //       span.classList.remove('active')
                //     })
                //   // 给当前点击的单词添加选中状态
                //   wordSpan.classList.add('active')

                //   // （可选）手动选中单词文本（方便复制等操作）
                //   const selection = window.getSelection()
                //   const range = document.createRange()
                //   range.selectNodeContents(wordSpan)
                //   selection.removeAllRanges()
                //   selection.addRange(range)
                // })

                fragment.appendChild(wordSpan)
              })

              // 插入最后剩余的非单词部分
              if (parts[partIndex]) {
                fragment.appendChild(document.createTextNode(parts[partIndex]))
              }

              // console.log( fragment, node );
              // console.log( '----------------------' );
              // 用处理后的片段替换原文本节点
              node.parentNode.replaceChild(fragment, node)
            }

            // 情况2：当前是元素节点（递归处理子节点，跳过图片、br等无需处理的标签）
            else if (node.nodeType === Node.ELEMENT_NODE) {
              // 排除不需要处理的标签（图片、换行等）
              const ignoreTags = ['IMG', 'BR', 'SCRIPT', 'STYLE', 'button']
              if (!ignoreTags.includes(node.tagName)) {
                // 遍历所有子节点（注意：childNodes是动态集合，需转为数组）
                Array.from(node.childNodes).forEach((child) => {
                  processNode(child)
                })
              }
            }
          }

          // 开始处理富文本容器的所有节点
          processNode(richTextContainer)
        }

        // 3. 执行处理函数
        wrapWordsWithClickableSpan()

        // （可选）点击容器空白处，取消所有单词的选中状态
        richTextContainer.addEventListener('click', () => {
          // console.log( document.querySelectorAll('.word-span.active') );
          document.querySelectorAll('.word-span.active').forEach((span) => {
            console.log( this );
            span.classList.remove('active')
          })
          // 清空文本选中状态
          window.getSelection().removeAllRanges()
        })
      })
    </script>
  </body>
</html>
